<?
/*
    This collection of functions (libary) is licensed under GPL2.
    See LICENSE or www.gnu.org for more details.

    Author: Henri Ranki 19.9.2001

    Functions related to rpc functionality.

    $Id: dzrpc.php,v 1.8 2005/11/10 19:31:04 jyry Exp $
*/

include_once("sodebug.php");
include_once("sogeneric.php");
include_once("sostring.php");
include_once("dzconst.php");


/** Remote procedure data
 */
class dzRpcData  {
    var $Function;      /** Remote procedure name */
    var $Host;          /** Host name where the procedure is located */
    var $Dir;           /** Directory where the procedure is located */
    var $Port = 80;          /** Port where to send the request */

    /** Check that the data is corect
     * Does also some fixing.
     * @return bool False if the data is wrong and it could not be fixed
     */
    function check() {
        /* Suppose all is ok! */
        $result = true;

        /* Check that path to starts with a slash. This is a strange feature
         * in xmlrpc library. The path must start with slash. */
        if (soSubStr($this->Dir, 0 ,1) != "/")
            $this->Dir = "/".$this->Dir;

        if ($this->Port == "") $this->Port = 80;

        /* Check that all information is given */
        if ($this->Function == "") $result = false;
        if ($this->Host == "") $result = false;

        return $result;
    } // check


    /** Dump the rpc data into a string
     * <:str    The rpc data
     */
    function dump() {
        $result = "rpc://:".$this->Port.":".$this->Host;
        $result .= $this->Dir."->".$this->Function;

        return $result;
    } // dump
} // class dzRpcData



/** Send a request to Kone using rpc
 * Sends the given Itu to specified Kone. Rpc info is specified with comma
 * separated string. Rpc info defines the host and directory where Kone
 * is located. In addition to that the port is defined.
 *
 * >$RpcInfo:dzRpcData  The remote procedure information
 * >$Itu:dzItu Itu to send to Kone
 * <:dzItu Result from Kone
 */
function sendRpcCall($RpcInfo, $Itu) {
    global $soDebugLevel;

    /* Check that the remote procedure data is proper. */
    if (!$RpcInfo->check()) {
        $result_itu = new dzItu();
        $result_itu->setBlockItem(DZI_CONTROL, DZK_RPC_ERROR, 667);
        $result_itu->setBlockItem(DZI_CONTROL, DZK_RPC_RESULT_MSG,
                                  "poor remote procedure information:".
                                  $RpcInfo->dump());
        return $result_itu;             // <-- EXIT!!!
    } // if (!$RpcInfo->check())

    /* Encode the request that will be sent */
    $encoded_itu = $Itu->encode();

    /* Add the name of the file where Kone is implemented to the
       path to Kone */
    if ($RpcInfo->Dir == "/") {
        $RpcInfo->Dir .= DZ_RPC_ENTRY_POINT;
    } else {
        $RpcInfo->Dir = soJoinStr($RpcInfo->Dir, "/", DZ_RPC_ENTRY_POINT);
    } // if ($RpcInfo->Dir == "/")

    soDebug("dzRpc: ".$RpcInfo->dump(), SOD_DET);

    /* Create rpc packet that carries the parameters to the rpc server */
    $rpc_msg = new xmlrpcmsg($RpcInfo->Function,
                             array(new xmlrpcval($encoded_itu, "string")));

    /* Create the rpc client. */
    $rpc_client = new xmlrpc_client($RpcInfo->Dir, $RpcInfo->Host, $RpcInfo->Port);

    /* Turn debug on if atleast detailed debug level is configured */
    if ($soDebugLevel > SOD_DET)
        $rpc_client->setDebug(1);
    else
        $rpc_client->setDebug(0);


    /* Finally send request to Kone using xmlrpc */
    $rpc_result = $rpc_client->send($rpc_msg, 30);

    if (soGetClass($rpc_result) != "xmlrpcresp") {
        soDebug("dzRpc: xmlrpc returned bad result. Rpc failed", SOD_HI_E);
        $result_itu = new dzItu();
        $result_itu->setBlockItem(DZI_CONTROL, DZK_RPC_ERROR, 666);
        $result_itu->setBlockItem(DZI_CONTROL, DZK_RPC_RESULT_MSG,
                                  "xmlrpc-library failed. Returned bad result.");
        return $result_itu;         // <-- EXIT!!!
    } // if (soGetClass($rpc_result)

    if (!$rpc_result->faultCode()) {
        $result_msg = $rpc_result->value();
        $result = $result_msg->scalarval();
        /* Decode the received string to itu */
        soDebug("dzRpc: rpc  ok:$result", SOD_DET);
        $result_itu = new dzItu($result);
    } else {
        soDebug("dzRpc: rpc failed ".$rpc_result->faultCode().": ".
                $rpc_result->faultString(), SOD_HI_E);
        $result_itu = new dzItu();
        $result_itu->setBlockItem(DZI_CONTROL, DZK_RPC_ERROR,
                                  $rpc_result->faultCode());
        $result_itu->setBlockItem(DZI_CONTROL, DZK_RPC_RESULT_MSG,
                                  $rpc_result->faultString());
    } // if $rpc_result

    return $result_itu;
} // sendRpcCall


/** Call defined remote procedure
 * All remote procedures are defined in the application ini. This
 * function fetch the information from the ini acording to the function
 * name given as a parameter.
 *
 * >$RpcFunctionName:str  Remote procedure name
 * >$Itu:dzItu Itu to send to Kone
 * <:dzItu Result from Kone
 */
function dzRpcCallParams($RpcFunctionName, $Itu) {
    /* Get rpc function info */
    $app_ini_name = "file://".dzGetIniPath(DZID_INI_FOLDER)."/".
                    dzGetIniItem(DZIS_GENERAL, "appname", "site").
                    ".ini";
    $app_ini = new soIniData($app_ini_name);
    if (!$app_ini->Error) {
        /* Finaly get the information of the specified remote function */
        $rpc_info = $app_ini->getValueSet("rpc", $RpcFunctionName);

        /* Fill in the rpc data structure */
        $rpc_data = new dzRpcData();
        $rpc_data->Host = $rpc_info->getItem("host");
        $rpc_data->Dir = $rpc_info->getItem("dir");
        $rpc_data->Port = $rpc_info->getItem("port");
        $rpc_data->Function = $RpcFunctionName;

        /* Send the request */
        $result_itu = sendRpcCall($rpc_data, $Itu);
    } else {
        dzSysError("Cant find application ini: $app_ini_name", "Dataxi");
        $result_itu = NULL;
    } // if

    return $result_itu;
} // dzRpcCallParams


/** Call defined remote procedure
 * >$FunctionDef:str    The function definition
 * >$SesData:dzSession  The curren session data
 * <:dzItu the result itu
 */
function dzRpcCall($FunctionDef, &$SesData) {
    soDebug("dzRpc: rpc=$FunctionDef", SOD_DET);
    /* Extract function name and parameters */
    $func_name = soExtractVal($FunctionDef, "(", 1);
    $params = soExplode(soParseVal($FunctionDef, "(", ")", 1), ",");

    /* Array that hold constant variables. */
    $constant_params = array();
    /* Loop all parameters and insert them into itu */
    foreach ($params as $param) {
        /* If a store is defined insert the store into itu. */
        if (soSubStr($param, 0, 1) == "$") {
            $store = soCaseConv(soSubStr($param, 1,
                                         strlen($param)-1),
                                "U");
            $data = $SesData->HotView->Laituri->getStore($store);
        } else {
            $constant_params[] = $param;
        } //
    } // foreach

    /* Create Itu that caries the parameters */
    $itu = new dzItu();
    $itu->setBlock(DZI_DATA, $data);
    $itu->setBlockItem(DZI_CONTROL, DZK_PARAMS, $constant_params);

    /* Finally do the RPC thing! */
    soDebug("dzRpc: rpc parameters ($store), ".
            implode(",", $constant_params), SOD_DET);
    soDebug("dzRpc: Call rpc. Function name: $func_name",
            SOD_DET);
    $result_itu = dzRpcCallParams($func_name, $itu);

    return $result_itu;
} // dzRpcCall


/** Check Itu for rpc error
 *  Call this function find out if there was in error in the rpc.
 *  @param object dzItu $Itu Itu received as a result from the rpc
 *  @return bool True if there were an error
 */
function isRpcError($Itu) {
    /* Check that the rpc was successfull */
    return $Itu->getBlockItemAt(DZI_CONTROL, DZK_RPC_ERROR);
} // isRpcError


/** get the the error text
 *  Use this function to get the plain text error description in case of
 *  rpc error
 *  @param dzItu $Itu Itu received as a result from the rpc
 *  @return string Error text
 */
function getRpcErrorText($Itu) {
    $error_code = $Itu->getBlockItemAt(DZI_CONTROL, DZK_RPC_ERROR);
    $fault_string = $Itu->getBlockItemAt(DZI_CONTROL, DZK_RPC_RESULT_MSG);
    if ($error_code == "")
        $result = "";
    else
        $result = "($error_code) $fault_string";

    return $result;
} // getRpcErrorText


/** Decode the parameter that received from the rpc call
 *  All real data is stored in dzItu that is encoded and delivered as
 *  url encoded string.
 *
 *  @param array/xmlrpcval $Params Encoded xmlrpc parameter
 *  @param string dzItu decoded parameter
 */
function decodeRpcParameter($Params) {
    /* Get sent dzItu packet. Itu-packet is sent as url encoded string. */
    $rpc_packet = $Params->params[0];
    $url_coded_itu = $rpc_packet->scalarval();

    /* Decode and return the itu got as parameter */
    return new dzItu($url_coded_itu);
} // decodeRpcParameter


/** encode the rpc result itu
 *  All real data is stored in dzItu that is encoded and delivered as
 *  url encoded string.
 *
 *  @param object dzItu >$Result Itu that contains the result
 *  @return string xmlrpcresp encoded parameter that is send back to the client
 */
function encodeRpcResult($Result) {
    if (soGetClass($Result != "soset")) {
        $result_packet = "";
    } else {
        $url_coded_result = $Result->encode();
        /* Create xml rpc result packet */
        $rpc_coded_result = new xmlrpcval($url_coded_result, "string");
        $result_packet = new xmlrpcresp($rpc_coded_result);
    } // get_class

    return $result_packet;
} // encodeRpcResult


/** get the rpc parameter array
 *
 *  @parasm object dzItu $Itu Itu got as parameter
 *  @param array/any Parameters for the remote function
 */
function getRpcArgv($Itu) {
    return $Itu->getBlockItem(DZI_CONTROL, DZK_PARAMS);
} // getRpcArgv


/** get the rpc parameter count
 *
 *  @param object dzItu $Itu Itu got as parameter
 *  @return int  The amount of parameters for the remote procedure
 */
function getRpcArgc($Itu) {
    return count($Itu->getBlockItem(DZI_CONTROL, DZK_PARAMS));
} // getRpcArgc


/** Get the result itu
 * Creates the result itu. Use this after  all processing is done in the
 * remote procedure
 *
 * NOTE! Do not use in case of subari rpc.
 *
 * >$Results:Array/str  Assosiative array. Key is the result field and the
 *                      value is the result value to put in the field.
 * <:soItu  Result that is returned to caller
 */
function getRpcResultItu($Results) {
    /* Create the itu where the result is set. */
    $result_set = new soSet();

    /* Set the return values to soSet that is sent as the payload. */
    foreach($Results as $key => $value) {
        /* Result values of the field should be stored in an array */
        if (!is_array($value)) {
            $value = array($value);
        } // if
        /* Set the return value for the requested field */
        $result_set->setItem($key, $value);
    } // foreach

    $result_itu = new dzItu();
    $result_itu->setBlockItem(DZI_CONTROL, DZK_RESULT, DZR_OK);
    $result_itu->setBlock(DZI_DATA, $result_set);

    return $result_itu;
} // getRpcResultItu


/** Get the result fields
 * Get the names of the fields that wait for a return value
 * from the remote procedure.
 * >$Itu:dzItu  Itu got as parameter
 * <:array/str Field names
 */
function getRpcReturnFields($Itu) {
    return $Itu->getBlockItem(DZI_CONTROL, DZK_RESULT_FIELDS);
} // getRpcReturnFields


/** Get the coordinates of the remote function
 * Return set that includes the address of the server that contains
 * the called remote function.
 * The information is read from application ini file.
 * >$FunctionName:str Name of the function to call
 * <:soSet/str  function coordinates
 */
function getRemoteFunctionInfo($FunctionName) {
    /* get the coordinates for the remote function from
       the application ini */

    $app_ini_name = "file://".dzGetIniPath(DZID_INI_FOLDER)."/".
                    dzGetIniItem(DZIS_GENERAL, "appname", "site").
                    ".ini";
    $app_ini = new soIniData($app_ini_name);
    if (!$app_ini->Error) {
        $result = $app_ini->getValueSet("rpc", $FunctionName);
    } else {
        dzSysError("Cant find application ini: $app_ini_name", "Dataxi");
        $result = NULL;
    }

    return $result;
} // getRemoteFunctionInfo


/** Expands the remote procedure call definition string
 * Inserts the server coordinates to call definition
 * >$FuncDef:str The definition from the source
 * <:str  function coordinates
 */
function completeRpcInfo($FuncDef) {
    if (soLocateStr($FuncDef, "=", 1) < 0)
        $FuncDef = soSwapStr($FuncDef, "rpc://", "rpc://=", 1, 1);

    $rpc_info = soExplode($FuncDef, "=");

    /* $rpc_info[0] = result fields,
    $rpc_info[1] = function_name(params )*/
    $function = soSubStr($rpc_info[1], 0,
                         soLocateStr($rpc_info[1], "(", 1));
    /* If no parameter part -> $rpc_inf[1] is the function name. */
    if ($function == "")
        $function = $rpc_info[1];
    $rpc_func_info = getRemoteFunctionInfo($function);
    if (!soIsNull($rpc_func_info)) {
        $host = $rpc_func_info->getItem("host");
        $port = $rpc_func_info->getItem("port");
        $dir = $rpc_func_info->getItem("dir");
    } // if

    /* Now put all information together and return it */
   return $rpc_info[0]."=$host,$port,$dir,".$rpc_info[1];
} // completeRpcInfo

?>
